はじめてのBlenderアドオン開発
Last Update: 2023.3.1
Blender 2.7
はじめてのBlenderアドオン開発
Blender 2.7
Last Update: 2023.3.1
3-2. キーボードのキーイベントを扱う
3-1節 ではマウスのイベントを扱う方法を説明しました。 マウスのイベントを扱うことができるようになると、キーボードのイベントも扱いたいと思いませんか? そこで本節では、キーボードのイベントを扱う方法を説明します。
キーボードのイベントを扱う方法を理解するため、本節で作成するアドオンは次のようなキーボードの入力情報を利用した機能を持つものとします。
キー | 処理 |
---|---|
[X] | X軸正方向に平行移動 |
[Shift] + [X] | X軸負方向に平行移動 |
[Y] | Y軸正方向に平行移動 |
[Shift] + [Y] | Y軸負方向に平行移動 |
[Z] | Z軸正方向に平行移動 |
[Shift] + [Z] | Z軸負方向に平行移動 |
1-5節 を参考にして以下のソースコードを入力し、ファイル名を sample_3_2.py
として保存してください。
import bpy
from bpy.props import BoolProperty, PointerProperty
from mathutils import Vector
bl_info = {
"name": "サンプル3-2: キーボードのキー入力に応じてオブジェクトを並進移動させる",
"author": "Nutti",
"version": (2, 0),
"blender": (2, 75, 0),
"location": "3Dビュー > プロパティパネル > オブジェクト並進移動",
"description": "キーボードからの入力に応じてオブジェクトを並進移動させるアドオン",
"warning": "",
"support": "TESTING",
"wiki_url": "",
"tracker_url": "",
"category": "Object"
}
# プロパティ
class TOM_Properties(bpy.types.PropertyGroup):
running = BoolProperty(
name="オブジェクト並進移動モード中",
description="オブジェクト並進移動モード中か?",
default=False
)
# オブジェクト並進移動モード時の処理
class TranslateObjectMode(bpy.types.Operator):
bl_idname = "object.translate_object_mode"
bl_label = "オブジェクト並進移動モード"
bl_description = "オブジェクト並進移動モードへ移行します"
def modal(self, context, event):
props = context.scene.tom_props
# 3Dビューの画面を更新
if context.area:
context.area.tag_redraw()
# キーボードのQキーが押された場合は、オブジェクト並進移動モードを終了
if event.type == 'Q' and event.value == 'PRESS':
props.running = False
print("サンプル3-2: 通常モードへ移行しました。")
return {'FINISHED'}
if event.value == 'PRESS':
value = Vector((0.0, 0.0, 0.0))
if event.type == 'X':
value.x = 1.0 if not event.shift else -1.0
if event.type == 'Y':
value.y = 1.0 if not event.shift else -1.0
if event.type == 'Z':
value.z = 1.0 if not event.shift else -1.0
# 選択中のオブジェクトを並進移動する
bpy.ops.transform.translate(value=value)
return {'RUNNING_MODAL'}
def invoke(self, context, event):
props = context.scene.tom_props
if context.area.type == 'VIEW_3D':
# 開始ボタンが押された時の処理
if props.running is False:
props.running = True
# modal処理クラスを追加
context.window_manager.modal_handler_add(self)
print("サンプル3-2: オブジェクト並進移動モードへ移行しました。")
return {'RUNNING_MODAL'}
else:
return {'CANCELLED'}
# UI
class OBJECT_PT_SOEM(bpy.types.Panel):
bl_label = "オブジェクト並進移動モード"
bl_space_type = "VIEW_3D"
bl_region_type = "UI"
def draw(self, context):
layout = self.layout
props = context.scene.tom_props
# 開始/停止ボタンを追加
if props.running is False:
layout.operator(
TranslateObjectMode.bl_idname, text="開始", icon="PLAY"
)
else:
layout.operator(
TranslateObjectMode.bl_idname, text="終了", icon="PAUSE"
)
def register():
bpy.utils.register_module(__name__)
sc = bpy.types.Scene
sc.tom_props = PointerProperty(
name="プロパティ",
description="本アドオンで利用するプロパティ一覧",
type=TOM_Properties
)
print("サンプル3-2: アドオン「サンプル3-2」が有効化されました。")
def unregister():
del bpy.types.Scene.tom_props
bpy.utils.unregister_module(__name__)
print("サンプル3-2: アドオン「サンプル3-2」が無効化されました。")
if __name__ == "__main__":
register()
1-5節 を参考に、作成したアドオンを有効化するとコンソールウィンドウに以下の文字列が出力されます。
サンプル3-2: アドオン「サンプル3-2」が有効化されました。
プロパティパネルを表示し、項目 [オブジェクト並進移動モード] が追加されていることを確認します。
有効化したアドオンの機能を使い、動作を確認します。
1 | [3Dビュー] エリアのプロパティパネル上の項目 [オブジェクト並進移動モード] に配置されている [開始] ボタンをクリックすると、オブジェクト並進移動モードに移行します。この時、コンソールウィンドウに次のメッセージが出力されます。サンプル3-2: オブジェクト並進移動モードへ移行しました。 |
2 | オブジェクト並進移動モードではキーボードのキーの組み合わせによりオブジェクトを並進移動することができます。 |
3 | キーボードの [Q] キーを押すことで、オブジェクト並進移動モードが終了します。オブジェクト並進移動モードを終了した時に、コンソールウィンドウに次のメッセージが表示されます。サンプル3-2: 通常モードへ移行しました。 |
1-5節 を参考にして有効化したアドオンを無効化すると、コンソールウィンドウに次のような文字列が出力されます。
サンプル3-2: アドオン「サンプル3-2」が無効化されました。
ソースコードをみるとわかると思いますが、変数名などの細かい部分やオペレータクラスの modal
メソッドの処理を除いて 3-1節 で説明した内容とほとんど同じです。 このため本節では、オペレータクラス SpecialObjectEditMode
の modal
メソッドの処理内容についてのみ説明します。
3-1節 に引き続き、本節のサンプルでも複数のクラス間でデータを共有します。 プロパティの定義方法については 3-1節 の説明を参照することとし、ここではサンプルで定義しているプロパティの一覧を示します。
プロパティ | 意味 |
---|---|
running |
オブジェクト並進移動モード中のときに、値が True となる |
3-1節 では、面の削除処理中であることとモーダルモード中であることが対応していましたが、本節のサンプルではオブジェクト並進移動モードがモーダルモードに対応します。 以降本節では、モーダルモードと書かれていたらオブジェクト並進移動モードとして読み進めて問題ありません。
modal
メソッドの最初の処理である [3Dビュー] エリアの更新処理までは、3-1節 と同様です。 本節では、[3Dビュー] エリアの更新処理までの説明をせずに、モーダルモードの終了処理から説明します。
本節のサンプルでは、キーボードの [Q] キーを押すとモーダルモードを終了します。 この処理を実現するため、キーボードのキーイベントが発生した時に呼び出される modal
メソッドの引数 event
のイベント情報を利用します。 [Q] キーが押された時に、モーダルモードを終了するためのコードを次に示します。
# キーボードのQキーが押された場合は、オブジェクト並進移動モードを終了
if event.type == 'Q' and event.value == 'PRESS':
props.running = False
print("サンプル3-2: 通常モードへ移行しました。")
return {'FINISHED'}
3-1節 でも説明しましたが、キーイベント発生時の変数 event
には、次のようなメンバ変数が格納されています。
メンバ変数 | 意味 |
---|---|
type |
発生したイベントの識別子 |
value |
イベントの値 |
キーボードの [Q] キーが押されたとき、event.type
には Q
、event.value
に PRESS
が保存されます。 本節のサンプルではこのことを利用し、event.type
が Q
、event.value
が PRESS
のときに、props.running
を False
に設定したあと {'FINISHED'}
を返すことでモーダルモードを終了します。
オブジェクト並進移動モードでは、利用するキーの状態を確認してオブジェクトを並進移動する必要があります。 次に示すコードにより、キーの入力を確認してオブジェクトの移動量を設定します。
if event.value == 'PRESS':
value = Vector((0.0, 0.0, 0.0))
if event.type == 'X':
value.x = 1.0 if not event.shift else -1.0
if event.type == 'Y':
value.y = 1.0 if not event.shift else -1.0
if event.type == 'Z':
value.z = 1.0 if not event.shift else -1.0
基本的にはモーダルモード終了処理と同様に、イベント情報 event
を利用します。 ただし、[Shift] キーを押した時に反対方向へ並進移動させる必要があるため、[Shift] キーが押されていることを判定する必要があります。
[Shift] キーが押されたか否かを判定するためには、event.shift
変数を参照します。 [Shift] キーが押されていると event.shift
に True
が設定されるため、event.shift
が True
か False
かで、移動量を 1.0
または -1.0
に設定します。
これで並進移動量を求める処理が完成しましたが、[Shift] キーが押されていることを判定するために event.shift
変数を利用することに疑問をもたれた方もいるかもしれません。 event.type
には、'LEFT_SHIFT'
や 'RIGHT_SHIFT'
といった [Shift] キーのイベントを扱うイベント情報が存在します。 しかし、本節のサンプルではあえて event.shift
変数を利用しています。
event.type
の代わりになぜ event.shift
変数を利用する必要があるのでしょうか? それは、[Shift] キーを押したり離したりしたときしか modal
メソッドが呼ばれず、1回の modal
メソッドの呼び出しで1つのキーのイベントしか扱うことができないためです。 例えば、[Shift] キー + [X] キーを押す場合、プログラムからは [Shift] キーを押した後に [X] キーを押したようにみえるため、[Shift] キーのイベントが発生した後に [X] キーのイベントが発生することになります。 仮に event.type
を用いて [Shift] キーが押されている状態を判定しようとすると、独自に「Shiftキーが押されている」状態を判断するための処理を作る必要があります。 このように、無駄な処理が増えてしまうことから本節のサンプルでは event.shift
変数を用いて [Shift] キーの情報を取得しています。
このように、キーが押されていることを判定するための変数は [Shift] キー以外にもあります。 次にその変数の一覧を示します。
変数 | キー |
---|---|
event.alt |
[Alt] |
event.ctrl |
[Ctrl] |
event.oskey |
(Macの) [Command] |
event.shift |
[Shift] |
キー入力から求めた移動量をもとに、オブジェクトを並進移動します。
# 選択中のオブジェクトを並進移動する
bpy.ops.transform.translate(value=value)
オブジェクトの並進移動は、bpy.ops.transform.translate
関数で行います。 引数 value
に並進移動量を Vector
クラスのインスタンスとして渡すことで、選択中のオブジェクトを並進移動します。 bpy.ops.transform.translate
関数には他にもいろいろな引数を渡すことができますが、ここでは割愛します。
3-1節 で紹介したサンプルの modal
メソッドでは {'PASS_THROUGH'}
を返していましたが、本節のサンプルでは {'RUNNING_MODAL'}
で返しています。 これは特殊オブジェクトモード時にキーを押した時にBlender本体の処理へイベントが伝搬しないようにするためです。 仮に modal
メソッドの戻り値を {'RUNNING_MODAL'}
から {'PASS_THROUGH'}
に変更してしまうと、Blenderにもキーイベントが発生してしまい、期待しない動作を引き起こしてしまいます。 例えば、Blenderが標準でオブジェクトの削除機能に割り当てている [X] キーを押した場合、Blenderの削除機能が起動してしまいます。
本節では、キーボードのキーイベントを扱う方法を紹介しました。 3-1節 のサンプルと比較すると、modal
メソッドまたは invoke
メソッドに渡されてくるイベント情報が格納された引数を用いる点で、マウスのイベントとキーボードのイベントの扱い方が、ほとんど同じであることが理解できたかと思います。
ユーザからの入力イベントを扱ってインタラクティブな機能を提供することは、これまでに紹介してきた execute
メソッドを単に実行するだけの処理と比べて、処理が複雑になりがちでバグも発生しやすくなります。 しかし、キーボードやマウスのイベントを適切に扱うことができるようになると、アドオンで実現できることが広がるため、より高度なアドオンを作ることができるようになるでしょう。
modal
メソッドや invoke
メソッドの引数に渡されてくるイベント情報を用いることにより、キーボードのキーイベントを扱うことができる